import os
import xml.etree.ElementTree as et
import numpy as np
import cv2


def num_samples(x):
    """
    返回类似数组的数据数量

    Parameters
    ----------
    x: array-like

    Returns
    -------

    """
    if not hasattr(x, '__len__') and not hasattr(x, 'shape'):
        if hasattr(x, '__array__'):
            x = np.asarray(x)
        else:
            raise TypeError("Expected sequence or array-like, got %s" %
                            type(x))
    if hasattr(x, 'shape'):
        if len(x.shape) == 0:
            raise TypeError("Singleton array %r cannot be considered"
                            " a valid collection." % x)
        return x.shape[0]
    else:
        return len(x)


def check_consistent_length(*arrays):
    """
    检查所有数组的第一维的长度是否相等

    Parameters
    ----------
    *arrays : list or tuple of input objects.
        Objects that will be checked for consistent length.
    """
    lengths = [num_samples(X) for X in arrays if X is not None]
    uniques = np.unique(lengths)
    if len(uniques) > 1:
        raise ValueError("Found input variables with inconsistent numbers of"
                         " samples: %r" % [int(l) for l in lengths])


def merge_dict(*dicts):
    """
    合并一系列字典，合并后的字典的key是所有字典的key的并集，其value取和

    Parameters
    ----------
    dicts: 字典列表

    Returns
    -------
    合并后的字典
    """
    res = {}
    for _dict in dicts:
        for key, value in _dict.items():
            if key not in res:
                res[key] = value
            else:
                res[key] += value
    return res


def add_element(root, name, value):
    """
    向xml元素树的 `root` 节点添加一个名为 `name`，值为 `value` 的子节点

    Parameters
    ----------
    root: 根节点
    name: 要添加的子节点的名字
    value: 要添加的子节点的值

    Returns
    -------

    """
    sub_element = et.SubElement(root, name)
    sub_element.text = value


def get_file_name(file_name):
    """
    获取文件名，不包含扩展名

    Parameters
    ----------
    file_name: 文件完整路径

    Returns
    -------

    """
    (_, temp_filename) = os.path.split(file_name)
    (shot_name, _) = os.path.splitext(temp_filename)
    return shot_name


def is_overlap(rect1, rect2):
    """
    判断矩形是否存在重叠

    Parameters
    ----------
    rect1: 矩形1，形如[axis1_min, axis2_min, axis1_max, axis2_max]
    rect2: 矩形2，形如[axis1_min, axis2_min, axis1_max, axis2_max]

    Returns
    -------

    """
    return not ((rect1[0] >= rect2[2]) or
                (rect1[1] >= rect2[3]) or
                (rect1[2] <= rect2[0]) or
                (rect1[3] <= rect2[1]))


def is_in_rect(point, rect):
    """
    判断点是否在矩形内部

    Parameters
    ----------
    point: 点，形如[axis1, axis2]
    rect: 矩形，形如[axis1_min, axis2_min, axis1_max, axis2_max]

    Returns
    -------

    """
    return (rect[0] <= point[0] <= rect[2]) and \
           (rect[1] <= point[1] <= rect[3])


def get_overlap_area(rect1, rect2):
    """
    计算两个矩形的重叠部分的面积，如果不重叠返回0

    Parameters
    ----------
    rect1: 矩形1，形如[axis1_min, axis2_min, axis1_max, axis2_max]
    rect2: 矩形2，形如[axis1_min, axis2_min, axis1_max, axis2_max]

    Returns
    -------

    """
    if not is_overlap(rect1, rect2):
        return 0.0
    xmin = max(rect1[0], rect2[0])
    ymin = max(rect1[1], rect2[1])
    xmax = min(rect1[2], rect2[2])
    ymax = min(rect1[3], rect2[3])
    return (xmax - xmin) * (ymax - ymin)


def get_point_from_box(boxes, weights=(0.5, 0.5)):
    """
    从一系列类似矩形的`box`中获取点

    Parameters
    ----------
    boxes: 一系列类似[axis1_min, axis2_min, axis1_max, axis2_max]的`box`
    weights: axis1_min 和 axis2_min的权值

    Returns
    -------

    """
    points = []
    for box in boxes:
        points.append(
            [weights[0] * box[0] + (1 - weights[0]) * box[2],
             weights[1] * box[1] + (1 - weights[1]) * box[3]])
    return points


def is_in_polygon(point, polygon):
    """
    判断一个点是否在多边形内部

    Parameters
    ----------
    point: 点，形如[axis1, axis2]
    polygon: 多边形，形如[[axis1_1, axis2_1],
                         [axis1_2, axis2_2],
                         [axis1_3, axis2_3]
                         ...]

    Returns
    -------

    """
    max_x, max_y, min_x, min_y = 0.0, 0.0, 1.0, 1.0
    for seat in polygon:
        min_x = min(min_x, seat[0])
        min_y = min(min_y, seat[1])
        max_x = max(max_x, seat[0])
        max_y = max(max_y, seat[1])
    if not is_in_rect(point, [min_x, min_y, max_x, max_y]):
        return False
    flag = False
    last_point = len(polygon) - 1
    for idx, vertex in enumerate(polygon):
        if (vertex[1] > point[1]) != (polygon[last_point][1] > point[1]):
            if point[0] < (vertex[0] + (polygon[last_point][0] - vertex[0]) * (point[1] - vertex[1]) / (
                    polygon[last_point][1] - vertex[1])):
                flag = not flag
        last_point = idx
    return flag


def cv_imread(file_path):
    cv_img = cv2.imdecode(np.fromfile(file_path, dtype=np.uint8), -1)
    return cv_img


def cv_imwrite(file_path, img):
    postfix = os.path.splitext(file_path)[1]
    cv2.imencode(postfix, img)[1].tofile(file_path)
